<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Curve</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
      /* 隐藏body窗口区域滚动条 */
    }
  </style>
  <!--引入three.js三维引擎-->
  <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
  <script src="http://www.yanhuangxueyuan.com/versions/threejsR92/examples/js/controls/OrbitControls.js"></script>
  <!-- <script src="./three.js"></script> -->
  <!-- <script src="http://www.yanhuangxueyuan.com/threejs/build/three.js"></script> -->
</head>

<body>
  <script>
    /**
     * 创建场景对象Scene
     */
    let scene = new THREE.Scene();
    
    //参数：0, 0圆弧坐标原点x，y  100：圆弧半径    0, 2 * Math.PI：圆弧起始角度
    let arc = new THREE.ArcCurve(0, 0, 100, 0, 2 * Math.PI);
    let points = arc.getPoints(50)
    let geometry = new THREE.Geometry()
    geometry.setFromPoints(points)
    let line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
        // 使用顶点颜色渲染模型
        vertexColors: THREE.VertexColors,
    }))
    
    scene.add(line); //网格模型添加到场景中
    
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    let R = 100; //圆弧半径
    let N = 50; //分段数量
    // 批量生成圆弧上的顶点数据
    for (let i = 0; i < N; i++) {
      let angle = 2 * Math.PI / N * i;
      let x = R * Math.sin(angle);
      let y = R * Math.cos(angle);
      geometry.vertices.push(new THREE.Vector3(x, y, 0));
    }
    // 插入最后一个点，line渲染模式下，产生闭合效果
    geometry.vertices.push(geometry.vertices[0])
    //材质对象
    //线条模型对象
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    line.position.set(0, 100, 0)
    scene.add(line); //线条对象添加到场景中
    
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    let p1 = new THREE.Vector3(50, 0, 50); //顶点1坐标
    let p2 = new THREE.Vector3(0, 50, 0); //顶点2坐标
    // 三维直线LineCurve3
    let lineCurve3 = new THREE.LineCurve3(p1, p2);
    geometry.setFromPoints(lineCurve3.getPoints(10))
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    line.position.set(0, 50, 0)
    scene.add(line); //线条对象添加到场景中
    
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    // 二维直线LineCurve
    let lineCurve = new THREE.LineCurve(new THREE.Vector2(-50, 0), new THREE.Vector2(0, 50));
    geometry.setFromPoints(lineCurve.getPoints(10))
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    line.position.set(0, 50, 0)
    scene.add(line); //线条对象添加到场景中
    
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    geometry.vertices.push(new THREE.Vector3(50, 0, 0), new THREE.Vector3(0, 50, 0))
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    line.position.set(0, 50, 0)
    scene.add(line); //线条对象添加到场景中
    
    
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    // 三维样条曲线  Catmull-Rom算法
    let curve = new THREE.CatmullRomCurve3([
      new THREE.Vector3(-50, 20, 90),
      new THREE.Vector3(-10, 40, 40),
      new THREE.Vector3(0, 0, 0),
      new THREE.Vector3(60, -60, 0),
      new THREE.Vector3(70, 0, 80)
    ]);
    // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
    geometry.setFromPoints(curve.getPoints(100));
    //材质对象
    //线条模型对象
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    scene.add(line); //线条对象添加到场景中
    
    
    curve = new THREE.QuadraticBezierCurve3(new THREE.Vector3(-80, 0, 0), new THREE.Vector3(0, 80, 0), new THREE.Vector3(80, 0, 0))
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
    geometry.setFromPoints(curve.getPoints(100));
    //材质对象
    //线条模型对象
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    scene.add(line); //线条对象添加到场景中
    
    curve = new THREE.CubicBezierCurve3(new THREE.Vector3(-80, 0, 0), new THREE.Vector3(-40, 80, 0), new THREE.Vector3(40, 80, 0), new THREE.Vector3(80, 0, 0))
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
    geometry.setFromPoints(curve.getPoints(100));
    //材质对象
    //线条模型对象
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    scene.add(line); //线条对象添加到场景中
    
    let curve1 = new THREE.CubicBezierCurve3(new THREE.Vector3(-80, 0, 0), new THREE.Vector3(-20, 120, 0), new THREE.Vector3(20, -120, 0), new THREE.Vector3(80, 0, 0))
    let curve2 = new THREE.ArcCurve(0, 0, 80, 0, 2 * Math.PI)
    let curvePath = new THREE.CurvePath()
    curvePath.curves.push(curve1, curve2)
    geometry = new THREE.Geometry(); //声明一个几何体对象Geometry
    // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
    geometry.setFromPoints(curvePath.getPoints(100));
    //材质对象
    //线条模型对象
    line = new THREE.Line(geometry, new THREE.LineBasicMaterial({
      color: 0x000000
    }));
    line.position.set(0, 0, 100)
    scene.add(line); //线条对象添加到场景中
    
    //创建管道成型的路径(3D样条曲线)
    let path = new THREE.CatmullRomCurve3([
      new THREE.Vector3(-10, -50, -50),
      new THREE.Vector3(10, 0, 0),
      new THREE.Vector3(8, 50, 50),
      new THREE.Vector3(-5, 0, 100)
    ]);
    // path:路径   40：沿着轨迹细分数  2：管道半径   25：管道截面圆细分数
    geometry = new THREE.TubeGeometry(path, 40, 20, 25, false)
    let mesh = new THREE.Line(geometry, new THREE.MeshBasicMaterial({
      color: 0x000000
    }));
    mesh.position.set(0, 100, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    
    // 创建线条一：直线
    let line1 = new THREE.LineCurve3(new THREE.Vector3(-85.35, -35.36),new THREE.Vector3(-50, 0, 0));
    // 重建线条2：三维样条曲线
    curve = new THREE.CatmullRomCurve3([new THREE.Vector3(-50, 0, 0), new THREE.Vector3(0, 50, 0), p4 = new THREE.Vector3(50, 0, 0)]);
    // 创建线条3：直线
    let line2 = new THREE.LineCurve3(p4 = new THREE.Vector3(50, 0, 0),new THREE.Vector3(85.35, -35.36));
    curvePath = new THREE.CurvePath();// 创建CurvePath对象
    curvePath.curves.push(line1, curve, line2);// 插入多段线条
    //通过多段曲线路径创建生成管道，CurvePath：管道路径
    geometry = new THREE.TubeGeometry(curvePath, 100, 5, 25, false);
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(0, 100, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    points = [
        new THREE.Vector2(50,60),
        new THREE.Vector2(25,0),
        new THREE.Vector2(50,-60)
    ];
    geometry = new THREE.LatheGeometry(points,30);
    let material=new THREE.MeshPhongMaterial({
        color:0x0000ff, //三角面颜色
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    });//材质对象
    mesh=new THREE.Mesh(geometry,material);//旋转网格模型对象
    mesh.position.set(150, 0, 0)
    scene.add(mesh);//旋转网格模型添加到场景中
    
    
    let shape_t = new THREE.Shape()
    shape_t.splineThru(points)
    points = shape_t.getPoints(5)
    geometry = new THREE.LatheGeometry(points,30);
    mesh=new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff, //三角面颜色
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));//旋转网格模型对象
    mesh.position.set(250, 0, 0)
    scene.add(mesh);//旋转网格模型添加到场景中    
    
    points = [
      new THREE.Vector2(-50, -50),
      new THREE.Vector2(-60, 0),
      new THREE.Vector2(0, 50),
      new THREE.Vector2(60, 0),
      new THREE.Vector2(50, -50),
      new THREE.Vector2(-50, -50),
    ]
    // 通过顶点定义轮廓
    let shape = new THREE.Shape(points);
    // shape可以理解为一个需要填充轮廓
    // 所谓填充：ShapeGeometry算法利用顶点计算出三角面face3数据填充轮廓
    geometry = new THREE.ShapeGeometry(shape, 50)
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(-150, 0, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    shape = new THREE.Shape();
    shape.splineThru(points);//顶点带入样条插值计算函数
    geometry = new THREE.ShapeGeometry(shape, 50)
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(-250, 0, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    shape = new THREE.Shape()
    shape.absarc(0,0,100,0,2*Math.PI);//圆弧轮廓
    geometry = new THREE.ShapeGeometry(shape, 5)
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(-150, 150, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    
    geometry = new THREE.ShapeGeometry(shape, 15)
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(-150, -150, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    shape = new THREE.Shape();
    // 四条直线绘制一个矩形轮廓
    shape.moveTo(0,0);//起点
    shape.lineTo(0,100);//第2点
    shape.lineTo(100,100);//第3点
    shape.lineTo(100,0);//第4点
    shape.lineTo(0,0);//第5点
    
    geometry = new THREE.ShapeGeometry(shape, 15)
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(150, -150, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    R = 50;
    // 绘制一个半径为R、圆心坐标(0, 0)的半圆弧
    shape.absarc(0, 0, R, 0, Math.PI);
    //从圆弧的一个端点(-R, 0)到(-R, -200)绘制一条直线
    shape.lineTo(-R, -200);
    // 绘制一个半径为R、圆心坐标(0, -200)的半圆弧
    shape.absarc(0, -200, R, Math.PI, 2 * Math.PI);
    //从圆弧的一个端点(R, -200)到(-R, -200)绘制一条直线
    shape.lineTo(R, 0);
    geometry = new THREE.ShapeGeometry(shape, 30)
    
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(150, -150, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    // 一个外轮廓圆弧嵌套三个内圆弧轮廓
    shape = new THREE.Shape(); //Shape对象
    //外轮廓
    shape.arc(0, 0, 100, 0, 2 * Math.PI);
    // 内轮廓1
    let path1 = new THREE.Path();
    path1.arc(0, 0, 40, 0, 2 * Math.PI);
    // 内轮廓2
    let path2 = new THREE.Path();
    path2.arc(80, 0, 10, 0, 2 * Math.PI);
    // 内轮廓3
    let path3 = new THREE.Path();
    path3.arc(-80, 0, 10, 0, 2 * Math.PI);
    //三个内轮廓分别插入到holes属性中
    shape.holes.push(path1, path2, path3);
    geometry = new THREE.ShapeGeometry(shape, 30)
    
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(150, 	150, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    // 矩形嵌套矩形或圆弧
    shape=new THREE.Shape();//Shape对象
    //外轮廓
    shape.moveTo(0,0);//起点
    shape.lineTo(0,100);//第2点
    shape.lineTo(100,100);//第3点
    shape.lineTo(100,0);//第4点
    shape.lineTo(0,0);//第5点

    //内轮廓
    path=new THREE.Path();//path对象
    // path.arc(50,50,40,0,2*Math.PI);//圆弧
    path.moveTo(20,20);//起点
    path.lineTo(20,80);//第2点
    path.lineTo(80,80);//第3点
    path.lineTo(80,20);//第4点
    path.lineTo(20,20);//第5点
    shape.holes.push(path);//设置内轮廓
    
    geometry = new THREE.ShapeGeometry(shape, 30)
    
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(150, 	250, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    shape=new THREE.Shape();
    shape.arc(-50,0,30,0,2*Math.PI);
    // 轮廓对象2
    let shape2=new THREE.Shape();
    shape2.arc(50,0,30,0,2*Math.PI);
    // 轮廓对象3
    let shape3=new THREE.Shape();
    shape3.arc(0,50,30,0,2*Math.PI);
    // 多个shape作为元素组成数组,每一个shpae可以理解为一个要填充的轮廓
    geometry = new THREE.ShapeGeometry([shape,shape2,shape3], 30)
    
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(0, 250, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    /**
     * 创建拉伸网格模型
     */
    shape = new THREE.Shape();
    /**四条直线绘制一个矩形轮廓*/
    shape.moveTo(0,0);//起点
    shape.lineTo(0,100);//第2点
    shape.lineTo(100,100);//第3点
    shape.lineTo(100,0);//第4点
    shape.lineTo(0,0);//第5点
    geometry = new THREE.ExtrudeGeometry(//拉伸造型
        shape,//二维轮廓
        {
            //拉伸参数
            amount:120,//拉伸长度
            bevelEnabled:false//无倒角
        })
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(0, 250, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    curve = new THREE.CubicBezierCurve3(new THREE.Vector3(-80, 0, 0), new THREE.Vector3(-40, 80, 0), new THREE.Vector3(40, 80, 0), new THREE.Vector3(80, 0, 0))
    geometry = new THREE.ExtrudeGeometry(//拉伸造型
        shape,//二维轮廓
        {
            //拉伸参数
            extrudePath:curve, //选择扫描轨迹
            bevelEnabled:false, //无倒角
            steps:50
        })
    mesh = new THREE.Mesh(geometry, new THREE.MeshPhongMaterial({
        color:0x0000ff,
        side:THREE.DoubleSide, //两面可见
        wireframe: true
    }));
    mesh.position.set(500, 250, 0)
    scene.add(mesh); //线条对象添加到场景中
    
    
    let axisHelper = new THREE.AxesHelper(250);
    scene.add(axisHelper);
    /**
     * 光源设置
     */
    //点光源
    let point = new THREE.PointLight(0xffff00);
    point.position.set(400, 200, 300); //点光源位置
    scene.add(point); //点光源添加到场景中
    //环境光
    let ambient = new THREE.AmbientLight(0x444444);
    scene.add(ambient);
    // console.log(scene)
    // console.log(scene.children)
    /**
     * 相机设置
     */
    let width = window.innerWidth; //窗口宽度
    let height = window.innerHeight; //窗口高度
    let k = width / height; //窗口宽高比
    let s = 200; //三维场景显示范围控制系数，系数越大，显示的范围越大
    //创建相机对象
    let camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
    camera.position.set(0, 300, 200); //设置相机位置
    camera.lookAt(scene.position); //设置相机方向(指向的场景对象)
    /**
     * 创建渲染器对象
     */
    let renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);//设置渲染区域尺寸
    renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
    document.body.appendChild(renderer.domElement); //body元素中插入canvas对象
    
    Game = {
		tickLength: 16, 				// 20Hz
		lastTick: performance.now(),	// 最后更新时间
		stopKey: 0,
		canvas: null,
		context: null,
    }
    
    window.onresize = function () {
        let width = window.innerWidth; //窗口宽度
        let height = window.innerHeight; //窗口高度
        renderer.setSize(width, height);//设置渲染区域尺寸

        k = width / height; //窗口宽高比
        //修改相机对象属性
        camera.left = -s * k
        camera.right = s * k
        camera.top = s
        camera.bottom = -s
        camera.aspect = k
        camera.updateProjectionMatrix()
    }

    function render(tFrame) {
        if (tFrame === undefined) {
            tFrame = performance.now()
            Game.lastTick = performance.now()
        }
        let nextTick = Game.lastTick + Game.tickLength
        let tickNum = 0
        if (tFrame > nextTick) {
            tickNum = Math.floor((tFrame - Game.lastTick) / Game.tickLength)
        }
        Game.lastTick = tFrame
        /*
        for (let num = 0; num < tickNum; ++num) {
            for (let i = 0, l = meshes.length; i < l; ++i) {
                meshes[i].position.set((i + 0.5 - l / 2) * 150, 0, 0)
                meshes[i].rotateY(3.14 / 180)
            }
        }
        */
        //执行渲染操作   指定场景、相机作为参数
        renderer.render(scene, camera);
        Game.stopKey = requestAnimationFrame(render)
    }
    render()
    let controls = new THREE.OrbitControls(camera,renderer.domElement);//创建控件对象
    controls.addEventListener('change', render);
  </script>
</body>
</html>
